package violin.web;

import violin.Configuration;
import violin.web.results.JsonResult;
import violin.web.results.VelocityResult;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class DispatcherServlet extends HttpServlet {
    private Configuration configuration;
    private Site site;

    @Override
    public void init() throws ServletException {
        super.init();
        configuration = new Configuration();
        site = new Site(configuration);
    }

    @Override
    @SuppressWarnings({"unchecked"})
    protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws ServletException, IOException {
        String uri = httpServletRequest.getRequestURI();

        try {
            HttpMethod httpMethod = resolveHttpMethod(httpServletRequest);

            Object returnValue;
            Resource resource = site.locate(uri);
            Method targetMethod = resource.resolveResourceMethod(httpMethod);
            Object resourceObject = resource.getResource();

            HttpParamsCollector paramsCollector = new HttpParamsCollector(targetMethod, resource.getPathParams(uri));
            Object[] params = paramsCollector.collect(httpServletRequest).params();

            returnValue = targetMethod.invoke(resourceObject, params);

            //set status code

            String result;
            String contentType = httpServletRequest.getHeader("content-type").toLowerCase();
            if ("text/json".equals(contentType)) {
                result = new JsonResult(returnValue).toString();
            } else if ("text/plain".equals(contentType)) {
                result = returnValue.toString();
            } else {
                result = new VelocityResult(resourceObject, returnValue, targetMethod).toString();
            }
            httpServletResponse.getWriter().write(result);
        } catch (IllegalAccessException e) {
            throw new UnsupportedOperationException();
        } catch (InvocationTargetException e) {
            throw new UnsupportedOperationException();
        }
    }

    private HttpMethod resolveHttpMethod(HttpServletRequest httpServletRequest) {
        String _httpMethod;
        String httpMethodParam = "_http_method";
        if (httpServletRequest.getParameter(httpMethodParam) != null) {
            _httpMethod = httpServletRequest.getParameter(httpMethodParam);
        } else if (httpServletRequest.getAttribute(httpMethodParam) != null) {
            _httpMethod = (String) httpServletRequest.getAttribute(httpMethodParam);
        } else {
            _httpMethod = httpServletRequest.getMethod();
        }
        _httpMethod = _httpMethod.toLowerCase();
        if (_httpMethod.equals("get")) {
            return HttpMethod.Get;
        } else if (_httpMethod.equals("post")) {
            return HttpMethod.Post;
        } else if (_httpMethod.equals("put")) {
            return HttpMethod.Put;
        } else if (_httpMethod.equals("delete")) {
            return HttpMethod.Delete;
        }
        return HttpMethod.Get;
    }

    @Override
    protected void doHead(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws ServletException, IOException {
        super.doHead(httpServletRequest, httpServletResponse);
    }

    @Override
    protected void doOptions(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws ServletException, IOException {
        super.doOptions(httpServletRequest, httpServletResponse);
    }

    @Override
    protected void doTrace(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws ServletException, IOException {
        super.doTrace(httpServletRequest, httpServletResponse);
    }

    @Override
    protected long getLastModified(HttpServletRequest httpServletRequest) {
        return super.getLastModified(httpServletRequest);
    }

    @Override
    protected void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws ServletException, IOException {
        doGet(httpServletRequest, httpServletResponse);
    }

    @Override
    protected void doPut(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws ServletException, IOException {
        doGet(httpServletRequest, httpServletResponse);
    }

    @Override
    protected void doDelete(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws ServletException, IOException {
        doGet(httpServletRequest, httpServletResponse);
    }

    public void bindResourceClass(Class<?> resourceClass) {
        this.site.bind(resourceClass);
    }
}
